<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
<script>
  const channel = new MessageChannel()
  let pendingCallback;
  let startTime;
  let timeoutTime;
  let perFrameTime = (1000 / 60);
  let timeRemaining = () => perFrameTime - (Date.now() - startTime);
  channel.port2.onmessage = () => {
    if (pendingCallback) {
      pendingCallback({ didTimeout: Date.now() > timeoutTime, timeRemaining });
    }
  }
  window.requestIdleCallback = (callback, options) => {
    timeoutTime = Date.now() + options.timeout;
    requestAnimationFrame((/*rafTime*//*←这里其实还有个rafTime可以用,这个时间是从页面刷新之后到现在的相对时间,你可以通过在浏览器输入performance.now获取到*/) => {
      startTime = Date.now();
      pendingCallback = callback;
      channel.port1.postMessage('hello'); //postMessage 是个宏任务, 当onmessage执行时, 说明浏览器有空闲了
    })
    /* startTime = Date.now();
    setTimeout(() => {
        callback({ didTimeout: Date.now() > timeoutTime, timeRemaining });
    }); */
  }

  function sleep(d) {
    for (var t = Date.now(); Date.now() - t <= d;);
  }
  const works = [
    () => {
      console.log("第1个任务开始");
      sleep(30);//sleep(20);
      console.log("第1个任务结束");
    },
    () => {
      console.log("第2个任务开始");
      sleep(30);//sleep(20);
      console.log("第2个任务结束");
    },
    () => {
      console.log("第3个任务开始");
      sleep(30);//sleep(20);
      console.log("第3个任务结束");
    },
  ];

  requestIdleCallback(workLoop, { timeout: 60 * 1000 });
  function workLoop(deadline) {
    console.log('本帧剩余时间', parseInt(deadline.timeRemaining()));
    while ((deadline.timeRemaining() > 1 || deadline.didTimeout) && works.length > 0) {
      performUnitOfWork();
    }
    if (works.length > 0) {
      console.log(`只剩下${parseInt(deadline.timeRemaining())}ms,时间片到了等待下次空闲时间的调度`);
      requestIdleCallback(workLoop, { timeout: 60 * 1000 });
    }
  }
  function performUnitOfWork() {
    works.shift()();
  }
</script>
</body>

</html>
